/*  -*- mode: c++; coding: utf-8; c-file-style: "stroustrup"; -*-

    Copyright 2008 Asier Aguirre <asier.aguirre@gmail.com>
    This file is part of Intromit.

    Intromit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Intromit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Intromit.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "render_obj_cube.hh"
#include "render_params.hh"
#include "render_obj_camera.hh"
#include "event_mng.hh"
#include "ogl.hh"
#include "time_queue.hh"
#include <math.h>

// DEFINES
#define SELECTOR_SOMETHING      IS_SEL_CENTER

#define COLOR_NONSELECT         0.20, 0.70, 0.30, 0.5
#define COLOR_SELECT            0.40, 1.00, 0.50, 0.5
#define COLOR_CSELECT           0.30, 0.90, 0.40, 0.5
#define COLOR_ANIM_USEC         150000


CLASS_INIT(render_obj_cube); // RENDER_OBJ_CUBE CLASS

render_obj_cube::render_obj_cube()
{
    CLASS_CTOR(render_obj_cube);
    ctor();
}

render_obj_cube::~render_obj_cube()
{
    CLASS_DTOR(render_obj_cube);
    dtor();
}

void render_obj_cube::ctor()
{
    dims[IS_X]=dims[IS_Y]=dims[IS_Z]=1.0;
    spin=false;

    // clear color
    color.set_color(COLOR_NONSELECT);
    render_color_anim::init_color(color_nonselect, COLOR_NONSELECT);
    render_color_anim::init_color(color_select,    COLOR_SELECT);
    render_color_anim::init_color(color_cselect,   COLOR_CSELECT);
    color_anim_usec=COLOR_ANIM_USEC;
}

void render_obj_cube::dtor()
{
}

bool render_obj_cube::init(render_mng *ctx)
{
    if(spin) ctx->get_queue()->add((time_queue_callback)event_queue_callback, this, ctx);
    return render_obj::init(ctx);
}

void render_obj_cube::render(render_mng *ctx)
{
    render_init(ctx);

    // specific cube render
    if(ctx->get_pass()==RENDER_PASS_PRECALC)
    {
	// get ogl modelview matrix, that will be used multiple times later
	double mv[IS_MAT4X4];
	get_modelview_matrix(mv);

	// precalc pass
	if(modelview_projection_visibility(0.5*RO_MODULE(dims), mv))
	    required_pass=1<<RENDER_PASS_OPAQUE | 1<<RENDER_PASS_SELECT;
	else
	    required_pass=0;

	// attraction field
	double dist;
	if((dist=sqrt(get_modelview_distance_nsqr(mv)))<20)
	{
	    // set action params
	    double m[IS_MAT4X4], separation=5.0;
	    ctx->calculate_hierarchy_modelview_nocam_matrix(m);
	    action_attraction_str *attraction=(action_attraction_str *)ctx->get_action_params(sizeof(action_attraction_str));
	    attraction->position[IS_X]=m[12]+m[8] *separation;
	    attraction->position[IS_Y]=m[13]+m[9] *separation;
	    attraction->position[IS_Z]=m[14]+m[10]*separation;
	    attraction->lookat[IS_X]=m[8];
	    attraction->lookat[IS_Y]=m[9];
	    attraction->lookat[IS_Z]=m[10];
	    attraction->vertical[IS_X]=m[4];
	    attraction->vertical[IS_Y]=m[5];
	    attraction->vertical[IS_Z]=m[6];
	    attraction->velocity=10/(dist+0.1);

	    // call action
	    ctx->event_action_from_render_obj(
		EM_SET_ACTION(EM_ACTION_TYPE_OBJ_CAMERA_ATTRACTOR,
			      EM_ACTION_SEQ_ONCE,
			      EM_ACTION_COORD_NONE,
			      EM_ACTION_CURSOR_NORMAL,
			      EM_ACTION_KEY_NORMAL),
		IS_TYPE_CAMERA, IS_STATUS_ANY);
	}
    }
    else if(ctx->get_pass()==RENDER_PASS_OPAQUE)
    {
	render_cube_complete();
	render_modifier(false);
    }
    else if(ctx->get_pass()==RENDER_PASS_SELECT)
    {
	render_cube();
	render_modifier(true);
    }

    render_children(ctx);
    render_finish(ctx);
}

void render_obj_cube::render_cube()
{
    // center cube
    glPushMatrix();
    glTranslated(-dims[0]*0.5, -dims[1]*0.5, -dims[2]*0.5);

    // draw cube
    glBegin(GL_QUADS);

    glVertex3d(0,       dims[1], 0);
    glVertex3d(dims[0], dims[1], 0);
    glVertex3d(dims[0], 0,       0);
    glVertex3d(0,       0,       0);

    glVertex3d(dims[0], 0,       dims[2]);
    glVertex3d(dims[0], dims[1], dims[2]);
    glVertex3d(0,       dims[1], dims[2]);
    glVertex3d(0,       0,       dims[2]);

    glVertex3d(dims[0], 0,       0);
    glVertex3d(dims[0], 0,       dims[2]);
    glVertex3d(0,       0,       dims[2]);
    glVertex3d(0,       0,       0);

    glVertex3d(0,       dims[1], dims[2]);
    glVertex3d(dims[0], dims[1], dims[2]);
    glVertex3d(dims[0], dims[1], 0);
    glVertex3d(0,       dims[1], 0);

    glVertex3d(0,       0,       dims[2]);
    glVertex3d(0,       dims[1], dims[2]);
    glVertex3d(0,       dims[1], 0);
    glVertex3d(0,       0,       0);

    glVertex3d(dims[0], dims[1], 0);
    glVertex3d(dims[0], dims[1], dims[2]);
    glVertex3d(dims[0], 0,       dims[2]);
    glVertex3d(dims[0], 0,       0);

    glEnd();

    glPopMatrix();
}

void render_obj_cube::render_cube_complete()
{
    // center cube
    glPushMatrix();
    glTranslated(-dims[0]*0.5, -dims[1]*0.5, -dims[2]*0.5);

    // color
    float color_tmp[IS_RGBA];
    if(color.refresh(color_tmp)) forced_pass=RENDER_PASS_OPAQUE; // require refresh
    glMaterialfv(GL_FRONT, GL_DIFFUSE, color_tmp);

    // draw cube
    glBegin(GL_QUADS);

    glNormal3d(0, 0, -1);

    glVertex3d(0,       dims[1], 0);
    glVertex3d(dims[0], dims[1], 0);
    glVertex3d(dims[0], 0,       0);
    glVertex3d(0,       0,       0);

    glNormal3d(0, 0, 1);

    glVertex3d(dims[0], 0,       dims[2]);
    glVertex3d(dims[0], dims[1], dims[2]);
    glVertex3d(0,       dims[1], dims[2]);
    glVertex3d(0,       0,       dims[2]);

    glNormal3d(0, -1, 0);

    glVertex3d(dims[0], 0,       0);
    glVertex3d(dims[0], 0,       dims[2]);
    glVertex3d(0,       0,       dims[2]);
    glVertex3d(0,       0,       0);

    glNormal3d(0, 1, 0);

    glVertex3d(0,       dims[1], dims[2]);
    glVertex3d(dims[0], dims[1], dims[2]);
    glVertex3d(dims[0], dims[1], 0);
    glVertex3d(0,       dims[1], 0);

    glNormal3d(-1, 0, 0);

    glVertex3d(0,       0,       dims[2]);
    glVertex3d(0,       dims[1], dims[2]);
    glVertex3d(0,       dims[1], 0);
    glVertex3d(0,       0,       0);

    glNormal3d(1, 0, 0);

    glVertex3d(dims[0], dims[1], 0);
    glVertex3d(dims[0], dims[1], dims[2]);
    glVertex3d(dims[0], 0,       dims[2]);
    glVertex3d(dims[0], 0,       0);

    // end drawing cube
    glEnd();

    glPopMatrix();
}

void render_obj_cube::render_modifier(bool select)
{
    if(select)
    {
	glPushName(SELECTOR_SOMETHING);
    }
    else
    {
	// color
	float color_side[IS_RGBA];
	for(int i=0; i<IS_RGBA; i++) color_side[i]=(float)color_nonselect[i]*(1.0/255.0)*1.1;
	glMaterialfv(GL_FRONT, GL_DIFFUSE, color_side);
    }

    // new side
    glBegin(GL_QUADS);
    glNormal3d(0, 0, 1);
    glVertex3d(dims[0]*0.5, 0,           dims[2]);
    glVertex3d(dims[0]*0.5, dims[1]*0.5, dims[2]);
    glVertex3d(0,           dims[1]*0.5, dims[2]);
    glVertex3d(0,           0,           dims[2]);
    glEnd();

    if(select)
    {
	glPopName();
    }
}

void render_obj_cube::set_spin(bool set, render_mng *ctx)
{
    if(!spin && set)
    {
	// set spinning as an event
	ctx->get_queue()->add((time_queue_callback)event_queue_callback, this, ctx);
    }
    spin=set;
}

int render_obj_cube::event_queue_callback(render_obj_cube *obj, render_mng *ctx)
{
    ctx->set_forced_pass(RENDER_PASS_PRECALC, obj);
    if(obj->spin)
    {
	obj->modelview.do_rotation(M_PI/60, 1, 0, 0);
	return 10000;
    }
    else return -1;
}

bool render_obj_cube::event(render_mng *ctx)
{
    render_obj::event(ctx);
    if(ctx->get_event_last())
    {
	// status event: IS_STATUS_*
	if(status & IS_STATUS_SELECTED)
	    color.set_color_dest(color_select, color_anim_usec);
	else if(status & IS_STATUS_CSELECTED)
	    color.set_color_dest(color_cselect, color_anim_usec);
	else
	    color.set_color_dest(color_nonselect, color_anim_usec);

	ctx->set_forced_pass(RENDER_PASS_OPAQUE, this);
    }
    else
    {
	// keyboard or mouse event

#if 0
	// params
	unsigned *params;
	int n_params=ctx->selection_obj_params(this, &params);
	if(n_params)
	{
	    printf("PARMAS: ");
	    for(int i=0; i<n_params; i++) printf("0x%08x ", params[i]);
	    printf("\n");
	}
#endif

	// extract event info
	event_mng *e=ctx->get_event();
	int time=e->get_event_last_time();
	if(e->get_event_last()==OGL_KEYBOARD_EVENT)
	{
	    char buffer[16];
	    e->key_to_string(e->get_key_last_code(), e->get_key_last_status(), buffer, 16);
	    printf("0x%08x -> KEY: 0x%02x 0x%04x %d %9d   [%s]\n",
		   time,
		   e->get_key_last_code(),
		   e->get_key_last_status(),
		   e->get_key_last_press(),
		   e->get_key_last_accu_time(),
		   buffer
		);
	}
	else if(e->get_event_last()==OGL_MOUSE_MOVE_EVENT)
	{
	    double v[IS_3D];
	    ctx->get_selection_touch_point(v);
	    printf("---- %9.3f %9.3f %9.3f\n", v[0], v[1], v[2]);
	}
    }
    return false; // not managed
}

unsigned render_obj_cube::get_type()
{
    return IS_TYPE_CUBE;
}
